#ifndef __UNIT_HPP__
#define __UNIT_HPP__ 1

#include <string>
#include <cstdint>

#include "UnitStats.hpp"
#include "Inventory.hpp"

using namespace std;

struct Stat;

/* @brief Enums */
enum class UpdateType : int32_t
{
	UPDATE_TYPE_RECALCSTATS =	1,
	UPDATE_TYPE_UNITPOS =		2,
};

enum class UnitType : int32_t
{
	UNSET =				0x00,
	PLAYER =			0x01,
	MONSTER =			0x02,
	MISSILE =			0x03,
	ITEM =				0x04,
	OBJECT =			0x05,
};

enum class PlayerClass : int32_t
{
	UNSET =			0,
	WIZARD =		1,
	HERETIC =		2,
	NECROMANCER =	3,
	RANGER =		4,
	GIANT =			5,
	WARRIOR =		6,
};

enum class ItemClass : int32_t
{
	UNSET =			0,
	SMALLTRINKET =	1,
	MEDIUMTRINKET =	2,
	LARGETRINKET =	3,
	RUNE =			4,
	JEWEL =			5,
	RING =			6,
	AMULET =			7,
	HELMET =			8,
	GLOVES =			9,
	BOOTS =			10,
	UPPERBODYARMOR =	11,
	LOWERBODYARMOR = 12,
	SHIELD =			13,
	DAGGER =			14,
	SWORD1H =		15,
	SWORD2H =		16,
	MACE1H =			17,
	MACE2H =			18,
	AXE1H =			19,
	AXE2H =			20,
	FLAIL =			21,
	SCEPTRE =		22,
	WAND =			23,
	WHIP1H =			24,
	WHIP2J =			25,
};

enum class PlayerSex : int32_t
{
	MALE,
	FEMALE,
};

enum class PlayerRace : int32_t
{
	HUMAN =			1,
	ELF,
	DWARF,
};

enum class PlayerMode : int32_t
{
	PLAYER_MODE_DEAD =			0,
	PLAYER_MODE_DEATH =			1,
	PLAYER_MODE_STAND =			2,
	PLAYER_MODE_WALK =			3,
	PLAYER_MODE_RUN =			4,
	PLAYER_MODE_BLOCK =			5,
	PLAYER_MODE_ATTACK =		6,
	PLAYER_MODE_CAST =			7,
	PLAYER_MODE_HIT =			8,
	PLAYER_MODE_KNOCKBACK =		9,
};

enum class MonsterMode : int32_t
{
	MONSTER_MODE_DEAD =			0,
	MONSTER_MODE_DEATH =		1,
	MONSTER_MODE_STAND =		2,
	MONSTER_MODE_WALK =			3,
	MONSTER_MODE_RUN =			4,
	MONSTER_MODE_BLOCK =		5,
	MONSTER_MODE_ATTACK =		6,
	MONSTER_MODE_CAST =			7,
	MONSTER_MODE_HIT =			8,
	MONSTER_MODE_KNOCKBACK =	9,
};

enum class MissileMode : int32_t
{
	MISSILE_MODE_INACTIVE =		0,
	MISSILE_MODE_ACTIVE =		1,
};

enum class ItemMode : int32_t
{
	ITEM_MODE_DROPPING =		0,
	ITEM_MODE_GROUND =			1,
	ITEM_MODE_CURSOR =			2,
	ITEM_MODE_EQUIPPED =		3,
	ITEM_MODE_STASHED =			4,
	ITEM_MODE_TRADING =			5,
};

/* @brief structs */

struct UnitPlayer { };
struct UnitMonster { };
struct UnitMissile { };
struct UnitItem { };
struct UnitObject { };

struct UnitData
{
	UnitType unitType;
	int32_t unitId;
	int32_t posX;
	int32_t posY;

	StatList statList;
	string unitName;

	UnitData() : unitId(0), posX(-1), posY(-1), unitName(string())
	{
		;;
	}

	~UnitData()
	{
		for(auto it(statList.begin()), end(statList.end()); it != end; ++it)
			delete it->second;
	}
};

template <typename S, typename enabled = void>
struct Unit { };

template <typename S>
struct Unit<S, typename enable_if<is_same<S, UnitPlayer>::value>::type> : UnitData
{
	PlayerClass playerClass;
	PlayerRace playerRace;
	PlayerSex playerSex;
	int32_t playerLevel;
	InventoryMap inventory;
	Unit()
	{
		this->unitType = UnitType::PLAYER;
		playerLevel = 0;
		playerClass = PlayerClass::UNSET;
		
	}
	~Unit()
	{
		for(auto it(inventory.begin()), end(inventory.end()); it != end; ++it)
			delete it->second;
	}
};

template <typename S>
struct Unit<S, typename enable_if<is_same<S, UnitMonster>::value>::type> : UnitData
{
	int32_t ownerId;
	int32_t monsterLevel;
	InventoryMap inventory;
	Unit()
	{
		this->unitType = UnitType::MONSTER;
		monsterLevel = 0;
		//monsterData->monsterClass = MONSTER_CLASS_UNSET;
	}
	~Unit()
	{
		for(auto it(inventory.begin()), end(inventory.end()); it != end; ++it)
			delete it->second;
	}
};

template <typename S>
struct Unit<S, typename enable_if<is_same<S, UnitMissile>::value>::type> : UnitData
{
	int32_t ownerId;
	int32_t missileLevel;
	int32_t velocity;
	Unit() { this->unitType = UnitType::MISSILE; }
};

template <typename S>
struct Unit<S, typename enable_if<is_same<S, UnitItem>::value>::type> : UnitData
{
	int32_t ownerId;
	int32_t isIdentified;
	int32_t location;
	ItemClass itemClass;
	int32_t itemLevel;
	Unit() { this->unitType = UnitType::ITEM; }
};

template <typename S>
struct Unit<S, typename enable_if<is_same<S, UnitObject>::value>::type> : UnitData
{
	int32_t objectLevel;
	Unit() { this->unitType = UnitType::OBJECT; }
};

#endif
